#include "ParallelPoison.h"
#include <stdlib.h>
#include <math.h>
#include <iostream>
#include <vector>

ParallelPoison::ParallelPoison(void)
{
	kTrial = 10;
	randTexIds = new GLuint[kTrial];

	s_subdivideProgram = new CgProgram("media//shaders//ParallelPoison.cg", "renderquad_VS", 
		"media//shaders//ParallelPoison.cg", "subdivide_PS");
	s_dartthrowProgram = new CgProgram("media//shaders//ParallelPoison.cg", "renderquad_VS", 
		"media//shaders//ParallelPoison.cg", "dartthrow_PS");
	M = NULL;
}

ParallelPoison::~ParallelPoison(void)
{
	delete[] randTexIds;
	glDeleteTextures(kTrial, randTexIds);
	glDeleteTextures(1, &randId);

	delete s_subdivideProgram;
	delete s_dartthrowProgram;
	if (M != NULL) delete M;
}


GLuint ParallelPoison::process(int w, int h, float rmin)
{
	int maxl = ceil(log(1.f*w)/log(2.0));
	if (maxl < ceil(log(1.f*h)/log(2.0))) maxl = ceil(log(1.f*h)/log(2.0));
	int dim = (int)pow(2.f, maxl);

	//initialize texture with dim
	if (M != NULL) delete M;
	M = new IteratedRenderTexture(dim, dim, false, GL_NEAREST);
	M->startRender();
	glClearColor(2.f, 2.f, 0.f, 0.f); glClear(GL_COLOR_BUFFER_BIT);
	M->endRender();

	float r = 1.f;
	int l = 0;
	float maxid;
	float currentid = 0.f;
	while (r > (rmin/1.41421f))
	{
		//initialize M(l) with M(l-1)
		maxid = pow(2.f, l) - 1.f;

		M->startRender();
		glViewport(0, 0, maxid + 1, maxid + 1);
		glClearColor(0.f, 0.f, 0.f, 0.f); glClear(GL_COLOR_BUFFER_BIT);
		s_subdivideProgram->SetFragmentTexParam("sampleMap", M->getTextureID());
		s_subdivideProgram->SetFragmentParam1f("level", l);
		s_subdivideProgram->Enable();
		glBegin(GL_QUADS);
		glVertex3f(-1,-1,0);
		glVertex3f(1,-1,0);
		glVertex3f(1,1,0);
		glVertex3f(-1,1,0);		
		glEnd();
		s_subdivideProgram->Disable();
		M->endRender();

		//random positions for dart throwing
		genRandomVal(l);

		//random ids
		//genRandomId(currentid, l);

		//throw dart
		for (int k = 0; k < kTrial; k++)
		{
			for (int pid = 0; pid < 9; pid++)
			{
				M->startRender();
				glViewport(0, 0, maxid+1, maxid+1);
				glClearColor(0.f, 0.f, 0.f, 0.f); glClear(GL_COLOR_BUFFER_BIT);
				s_dartthrowProgram->SetFragmentTexParam("sampleMap", M->getTextureID());
				s_dartthrowProgram->SetFragmentTexParam("randMap", randTexIds[k]);
				s_dartthrowProgram->SetFragmentTexParam("randId", randId);
				s_dartthrowProgram->SetFragmentParam2i("pgroup", __PGORDER[pid][0], __PGORDER[pid][1]);
				s_dartthrowProgram->SetFragmentParam1f("level", l);
				s_dartthrowProgram->SetFragmentParam1f("rmin", rmin);
				s_dartthrowProgram->SetFragmentParam1f("maxid", maxid);
				s_dartthrowProgram->Enable();
				glBegin(GL_QUADS);
				glVertex3f(-1,-1,0);
				glVertex3f(1,-1,0);
				glVertex3f(1,1,0);
				glVertex3f(-1,1,0);		
				glEnd();
				s_dartthrowProgram->Disable();
				M->endRender();

				//test
				//{
				//	int size = maxid + 1;
				//	float* result = (float*)malloc(4*size*size*sizeof(float));
				//	M->startCopy();
				//	glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
				//	glReadPixels(0, 0, size, size, GL_RGBA, GL_FLOAT, result);
				//	M->endCopy();
				//	printf("level: %d\n", l);
				//	for (int j = size-1; j >= 0; j--)
				//	{
				//		for (int k = 0; k < size ; k++)
				//		{
				//			printf("(%4.2f,%4.2f,%4.2f,%4.2f),",result[4*(j*size + k)], result[4*(j*size + k) + 1],
				//				result[4*(j*size + k) + 2], result[4*(j*size + k) + 3]);
				//			//printf("(%4.2f),",result[j*size + 4*k+3]);
				//			
				//		}
				//		printf("\n");
				//	}
				//	free(result);
				//}

			}
		}

		//test
		//{
		//	int size = maxid + 1;
		//	float* result = (float*)malloc(4*size*size*sizeof(float));
		//	M->startCopy();
		//	glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
		//	glReadPixels(0, 0, size, size, GL_RGBA, GL_FLOAT, result);
		//	M->endCopy();
		//	printf("level: %d\n", l);
		//	for (int j = size-1; j >= 0; j--)
		//	{
		//		for (int k = 0; k < size ; k++)
		//		{
		//			printf("(%4.2f,%4.2f,%4.2f,%4.2f),",result[4*(j*size + k)], result[4*(j*size + k) + 1],
		//				result[4*(j*size + k) + 2], result[4*(j*size + k) + 3]);
		//			
		//		}
		//		printf("\n");
		//	}
		//	free(result);
		//}

		l++;
		r = r / 2.f;

	}

	printf("level: %d\n", l);

	//generate list of vertices
	int size = maxid + 1;
	float* result = (float*)malloc(4*size*size*sizeof(float));
	M->startCopy();
	glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glReadPixels(0, 0, size, size, GL_RGBA, GL_FLOAT, result);
	M->endCopy();
	GLuint vlist = glGenLists(1);
	glNewList(vlist, GL_COMPILE);
	glBegin(GL_POINTS);
	for (int j = 0; j < size; j++)
	{
		for (int k = 0; k < size ; k++)
		{
			float x = result[4*(j*size + k)] * dim;
			float y = result[4*(j*size + k) + 1] * dim;
			float level = result[4*(j*size + k) + 2];
			float a = result[4*(j*size + k) + 3];
			if (a > 0 && x < w && y < h)
			{
				//glVertex3f(x + 0.5f, y + 0.5f, level / (size*size));
				glVertex3f(x + 0.5f, y + 0.5f, level / 12.f);
			}
		}
	}
	glEnd();
	glEndList();
	free(result);
	return vlist;
}

void ParallelPoison::genRandomVal(int l)
{
	//generate k random textures
	int ncell = (int)pow(2.0, l);
	GLfloat* randval = new GLfloat[ncell*ncell*4];
	//float minid = 0;
	//if (l > 0) minid = pow(2.0, 2*(l-1));
	//float maxid = ncell*ncell - 1;

	glDeleteTextures(kTrial, randTexIds);
	glGenTextures(kTrial, randTexIds);
	for (int i = 0; i < kTrial; i++)
	{
		//float count = minid;
		for (int j = 0; j < ncell; j++)
		{
			for (int k = 0; k < ncell; k++)
			{
				randval[4*(j*ncell + k)] = (float)rand() / RAND_MAX;
				randval[4*(j*ncell + k) + 1] = (float)rand() / RAND_MAX;
				randval[4*(j*ncell + k) + 2] = 0.f;//count / (1024.f*1024.f);
				randval[4*(j*ncell + k) + 3] = 0.f;
				//if (count < maxid)
					//count++;
			}
		}

		glBindTexture(GL_TEXTURE_RECTANGLE, randTexIds[i]);
		glTexParameterf(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameterf(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, ncell,
			ncell, 0, GL_RGBA, GL_FLOAT, randval);
		glBindTexture(GL_TEXTURE_RECTANGLE, 0);

		//test
		//{
		//	int size = ncell;
		//	float* result = (float*)malloc(4*size*size*sizeof(float));
		//	GLuint sharedFBO;
		//	glGenFramebuffersEXT(1, &sharedFBO);
		//	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, sharedFBO);
		//	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_EXT,
		//		randTexIds[i], 0);
		//	glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
		//	glReadPixels(0, 0, size, size, GL_RGBA, GL_FLOAT, result);
		//	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
		//	glDeleteFramebuffers(1, &sharedFBO);
		//	printf("trial: %d\n", i);
		//	for (int j = size-1; j >= 0; j--)
		//	{
		//		for (int k = 0; k < size ; k++)
		//		{
		//			printf("(%4.2f,%4.2f,%4.2f,%4.2f),",result[4*(j*size + k)], result[4*(j*size + k) + 1],
		//				result[4*(j*size + k) + 2], result[4*(j*size + k) + 3]);
		//			//printf("(%4.2f),",result[j*size + 4*k+3]);
		//			
		//		}
		//		printf("\n");
		//	}
		//	free(result);
		//}
	}


	delete[] randval;
}

void ParallelPoison::genRandomId(float &cid, int level)
{
	glDeleteTextures(1, &randId);
	glGenTextures(1, &randId);
	int ncell = pow(2.0, level);
	GLfloat* randval = new GLfloat[ncell*ncell];

	std::vector<float> ids;
	int N = ncell*ncell;
	for (int i = 1; i <= N; i++)
		ids.push_back((float)i);
	cid = N;

	for (int j = 0; j < ncell; j++)
	{
		for (int k = 0; k < ncell; k++)
		{
			int pos = rand() % ids.size();
			randval[j*ncell + k] = ids[pos] / (1024.f*1024.f);
			ids.erase(ids.begin()+pos);
		}
	}

	glBindTexture(GL_TEXTURE_RECTANGLE, randId);
	glTexParameterf(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameterf(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, ncell,
		ncell, 0, GL_RED, GL_FLOAT, randval);
	glBindTexture(GL_TEXTURE_RECTANGLE, 0);

	//test
	//{
	//	int size = ncell;
	//	float* result = (float*)malloc(4*size*size*sizeof(float));
	//	GLuint sharedFBO;
	//	glGenFramebuffersEXT(1, &sharedFBO);
	//	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, sharedFBO);
	//	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_EXT,
	//		randId, 0);
	//	glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
	//	glReadPixels(0, 0, size, size, GL_RGBA, GL_FLOAT, result);
	//	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
	//	glDeleteFramebuffers(1, &sharedFBO);
	//	for (int j = size-1; j >= 0; j--)
	//	{
	//		for (int k = 0; k < size ; k++)
	//			printf("%4.2f\t",result[4*(j*size + k)]);
	//		printf("\n");
	//	}
	//	free(result);
	//}
}